using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace Irc.Objects
{
	public enum ModeName
	{
		None,
		TopicOperatorOnly,
		NoExternalMessages,
		InviteOnly,
		Moderated,
		Private,
		Secret,
		Key,
		Registered,
		Limit,
		HiddenHostmask,
		Owner,
		Protected,
		Operator,
		HalfOperator,
		Voice,
		Devoice
	}
	[CLSCompliant(true)]
	public class Mode
	{
		public Mode(Target target, ModeName modes)
		{
			this.target = target;
			mode = modes;
		}
		private Target target;
		private ModeName mode;

		public override bool Equals(object obj)
		{
			Mode m = obj as Mode;
			if(m == null) return false;
			return m.ModeType == mode && m.Target == target;
		}
		public override int GetHashCode()
		{
			return base.GetHashCode();
		}
		public Target Target {
			internal set { target = value; }
			get { return target; }
		}
		public ModeName ModeType
		{
			internal set { mode = value; }
			get { return mode; }
		}
	}
	[CLSCompliant(true)]
	public class NicknameMode : Mode
	{
		public NicknameMode(Target target, ModeName modes, Nickname param) : base(target, modes)
		{
			parameter = param;
		}
		public override bool Equals(object obj)
		{
			NicknameMode m = obj as NicknameMode;
			if(m == null) return false;
			return m.Parameter == parameter && base.Equals(obj);
		}
		public override int GetHashCode()
		{
			return base.GetHashCode();
		}
		private Nickname parameter;
		public Nickname Parameter {
			get { return parameter; }
		}
	}
	[CLSCompliant(true)]
	public class IntMode : Mode
	{
		public IntMode(Target target, ModeName modes, int param) : base(target, modes)
		{
			parameter = param;
		}
		public override bool Equals(object obj)
		{
			IntMode m = obj as IntMode;
			if(m == null) return false;
			return m.Parameter == parameter && base.Equals(obj);
		}
		public override int GetHashCode()
		{
			return base.GetHashCode();
		}
		private int parameter;
		public int Parameter {
			get { return parameter; }
		}
	}
	[CLSCompliant(true)]
	public class StringMode : Mode
	{
		public StringMode(Target target, ModeName modes, string param) : base (target, modes)
		{
			parameter = param;
		}
		public override bool Equals(object obj)
		{
			StringMode m = obj as StringMode;
			if(m == null) return false;
			return m.Parameter == parameter && base.Equals(obj);
		}
		public override int GetHashCode()
		{
			return base.GetHashCode();
		}
		private string parameter;
		public string Parameter {
			get { return parameter; }
		}
	}
	[CLSCompliant(true)]
	public class ModeCollection : IComparable<ModeCollection>, IEnumerable<Mode>
	{
		private List<Mode> modes = new List<Mode>();
		public Collection<Mode> ModeList {
			internal set
			{
				lock(this)
				{
					modes.Clear();
					modes.AddRange(value);
				}
			}
			get { return new Collection<Mode>(modes); }
		}
		public ModeCollection Add(Mode mode)
		{
			return this + mode;
		}
		public ModeCollection Subtract(Mode mode)
		{
			return this - mode;
		}
		public static ModeCollection operator +(ModeCollection container, Mode mode)
		{
			ModeCollection cont = new ModeCollection();
			cont.modes.AddRange(container.ModeList);
			cont.modes.Add(mode);
			return cont;
		}
		public static ModeCollection operator -(ModeCollection container, Mode mode)
		{
			ModeCollection cont = new ModeCollection();
			cont.modes.AddRange(container.ModeList);
			cont.modes.Remove(mode);
			return cont;
		}
		public static implicit operator string(ModeCollection modes)
		{
			if(modes != null)
				return modes.ToString();
			else return String.Empty;
		}
		public Mode this[int index]
		{
			get
			{
				return new Mode(modes[index].Target, modes[index].ModeType);
			}
		}
		public static bool operator ==(ModeCollection cont1, ModeCollection cont2)
		{
			// prevent infinite recursion
			if(((object)cont1) == null || ((object)cont2) == null)
				if(((object)cont1) == null && ((object)cont2) == null)
					return true;
				else
					return false;
			foreach(Mode mode in cont1.ModeList)
				if(cont2.modes.Find(delegate(Mode m){return m.Equals(mode);}) == null)
					return false;
			return true;
		}
		public static bool operator !=(ModeCollection cont1, ModeCollection cont2)
		{
			return !(cont1 == cont2);
		}
		public override int GetHashCode()
		{
			return base.GetHashCode();
		}
		public override bool Equals(object obj)
		{
			return this == (ModeCollection)obj;
		}
		public bool Contains(ModeName mode)
		{
			foreach(Mode m in ModeList)
				if(m.ModeType == mode)
					return true;
			return false;
		}
		public override string ToString()
		{
			return ToString("G", System.Globalization.CultureInfo.CurrentCulture);
		}
		public string ToString(string format)
		{
			return ToString(format, System.Globalization.CultureInfo.CurrentCulture);
		}
		public string ToString(string format, IFormatProvider formatProvider)
		{
			if(ModeMap.Instance.ModeToCharTable.Count == 0 || ModeMap.Instance.ModeToSymbolTable.Count == 0)
				throw new FormatException("Tables unavailable!");
			string ret = String.Empty;
			switch(format)
			{
				case "G": case null:
					foreach(Mode mode in modes)
					{
						if(mode is NicknameMode)
							ret += ModeMap.Instance.ModeToSymbolTable[mode.ModeType];
						else
							ret += ModeMap.Instance.ModeToCharTable[mode.ModeType];
					}
					break;
				case "S": case "s":
					if(Contains(ModeName.None)) ret = "";
					if(Contains(ModeName.Devoice)) ret = "-";
					if(Contains(ModeName.Voice)) ret = "+";
					if(Contains(ModeName.HalfOperator)) ret = "%";
					if(Contains(ModeName.Operator)) ret = "@";
					if(Contains(ModeName.Protected)) ret = "&";
					if(Contains(ModeName.Owner)) ret = "~";
					break;
				default:
					throw new ArgumentException(String.Format(formatProvider, Strings.NicknameFormatException, format), "format");
			}
			return ret;
		}
		public IEnumerator<Mode> GetEnumerator()
		{
 			return new ModeContainerEnumerator(this);
		}
		public int CompareTo(ModeCollection other)
		{
 			return (other == this ? 0 : ModeList.Count - other.ModeList.Count);
		}
		public static int operator >(ModeCollection cont1, ModeCollection cont2)
		{
			if(cont1 == null)
				return -1;
			return cont1.CompareTo(cont2);
		}
		public static int operator <(ModeCollection cont1, ModeCollection cont2)
		{
			if(cont2 == null)
				return 1;
			return cont2.CompareTo(cont1);
		}
		IEnumerator IEnumerable.GetEnumerator()
		{
			return new ModeContainerEnumerator(this);
		}
	}
	[CLSCompliant(true)]
	public sealed class ModeContainerEnumerator : IEnumerator<Mode>, IEnumerator
	{
		private int index;
		private ModeCollection container;
		internal ModeContainerEnumerator(ModeCollection cont) { container = cont; }
		public void Dispose() {  }
		public void Reset() { index = 0; }
		public Mode Current { get { return container.ModeList[index]; } }
		object IEnumerator.Current { get { return (object)Current; } }
		public bool MoveNext() { index++; if(index == container.ModeList.Count) return false; return true; }
	}
}
